/*
 * YAFFS: Yet another FFS. A NAND-flash specific file system.
 *
 * Copyright (C) 2002-2011 Aleph One Ltd.
 *   for Toby Churchill Ltd and Brightstar Engineering
 *
 * Created by Charles Manning <charles@aleph1.co.uk>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */


#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

#include "yaffsfs.h"

#include "yaffs_guts.h" /* Only for dumping device innards */

#define YAFFS_TEST_MOUNT_POINT "/yaffs2"

extern unsigned long tickGet(void);
extern int fsl_nand_chip_erase(void);

int dump_file_data(const char *fn)
{
	int h;
	int i = 0;
	unsigned char b;

	printf("\ndump file %s\n", fn);

	h = yaffs_open(fn, O_RDONLY,0);
	if (h < 0) {
		printf("open file %s failed\n", fn);
		return -1;
	}

	while (yaffs_read(h, &b, 1) > 0) {
		printf(" %02x",b);
		i++;
		if(i > 32) {
		   printf("\n");
		   i = 0;;
		 }
	}

	printf("\n");
	yaffs_close(h);

	return 0;
}

int copy_in_a_file(const char *yaffsName, const char *inName)
{
	int inh,outh;
	unsigned char buffer[100];
	int ni,no;
	unsigned size = 0;
	unsigned int startTicks = 0;
	unsigned int endTicks = 0;

	printf("\ncopy %s to %s\n", inName, yaffsName);

	startTicks = tickGet();

	inh = open(inName, O_RDONLY, S_IREAD);
	if (inh < 0) {
		printf("open file %s failed\n", inName);
		return -1;
	}

	outh = yaffs_open(yaffsName, O_CREAT | O_RDWR | O_TRUNC, S_IREAD | S_IWRITE);
	if (outh < 0) {
		printf("open file %s failed\n", yaffsName);
		close(inh);
		return -1;
	}

	while((ni = read(inh,buffer,100)) > 0) {
		no = yaffs_write(outh,buffer,ni);
		size += no;
		if(ni != no) {
			printf("problem writing yaffs file\n");
			yaffs_close(outh);
			close(inh);
			return -1;
		}
	}

	yaffs_close(outh);
	close(inh);

	endTicks = tickGet();

	printf("\r\nwriting yaffs file over, size = %d, time = %ds\r\n",
		size, (endTicks-startTicks)/60);

	return 0;
}


int copy_out_a_file(const char *outName, const char *yaffsName)
{
	int inh,outh;
	unsigned char buffer[100];
	int ni,no;
	unsigned size = 0;
	unsigned int startTicks = 0;
	unsigned int endTicks = 0;

	printf("\ncopy %s to %s\n", yaffsName, outName);

	startTicks = tickGet();

	inh = yaffs_open(yaffsName, O_RDONLY, S_IREAD);
	if (inh < 0) {
		printf("open file %s failed\n", yaffsName);
		close(inh);
		return -1;
	}

	outh = open(outName, O_CREAT | O_RDWR, S_IREAD | S_IWRITE);
	if (outh < 0) {
		printf("open file %s failed\n", outName);
		return -1;
	}

	while((ni = yaffs_read(inh,buffer,100)) > 0) {
		no = write(outh,buffer,ni);
		size += no;
		if(ni != no) {
			printf("problem writing file\n");
			yaffs_close(outh);
			close(inh);
			return -1;
		}
	}

	close(outh);
	yaffs_close(inh);

	endTicks = tickGet();

	printf("\r\nwriting yaffs file over, size = %d, time = %ds\r\n",
		size, (endTicks-startTicks)/60);

	return 0;
}


int make_a_file(const char *yaffsName, char bval, int sizeOfFile)
{
	int outh;
	int i;
	unsigned char buffer[100];

	printf("\nmake a file %s, size %d, bval %c\n", yaffsName, sizeOfFile, bval);

	outh = yaffs_open(yaffsName, O_CREAT | O_RDWR | O_TRUNC, S_IREAD | S_IWRITE);
	if (outh < 0) {
		printf("open file %s failed\n", yaffsName);
		return -1;
	}

	memset(buffer, bval, 100);

	do{
		i = sizeOfFile;
		if(i > 100) i = 100;
		sizeOfFile -= i;

		yaffs_write(outh, buffer, i);

	} while (sizeOfFile > 0);

	yaffs_close(outh);

	return 0;
}

int make_pattern_file(const char *fn, int size)
{
	int outh;
	int marker;
	int i;

	printf("\nmake a pattern file %s, size %d\n", fn, size);

	outh = yaffs_open(fn, O_CREAT | O_RDWR | O_TRUNC, S_IREAD | S_IWRITE);
	if (outh < 0) {
		printf("open file %s failed\n", fn);
		return -1;
	}

	for(i = 0; i < size; i+=256) {
		marker = ~i;
		yaffs_lseek(outh, i, SEEK_SET);
		yaffs_write(outh, &marker, sizeof(marker));
	}

	yaffs_close(outh);

	return 0;
}

int check_pattern_file(const char *fn)
{
	int h;
	int marker;
	int i;
	int size;
	int ok = 1;

	printf("\ncheck a pattern file %s\n", fn);

	h = yaffs_open(fn, O_RDWR, 0);
	if (h < 0) {
		printf("open file %s failed\n", fn);
		return -1;
	}

	size = yaffs_lseek(h, 0, SEEK_END);

	for (i = 0; i < size; i+=256) {
		yaffs_lseek(h, i, SEEK_SET);
		yaffs_read(h, &marker, sizeof(marker));
		ok = (marker == ~i);
		if (!ok) {
			printf("pattern check failed on file %s, size %d at position %d. Got %x instead of %x\n",
					fn, size, i, marker, ~i);
		}
	}

	yaffs_close(h);

	return 0;
}

void dump_directory_tree_worker(const char *dname,int recursive)
{
	yaffs_DIR *d;
	struct yaffs_dirent *de;
	struct yaffs_stat s;
	char str[1000];

	d = yaffs_opendir(dname);

	if(!d)
	{
		printf("opendir failed\n");
	}
	else
	{
		while((de = yaffs_readdir(d)) != NULL)
		{
			sprintf(str,"%s/%s",dname,de->d_name);

			yaffs_lstat(str,&s);

			printf("%s inode %ld obj %x length %ld mode %X ",
				str,s.st_ino,de->d_dont_use, s.st_size,s.st_mode);
			switch(s.st_mode & S_IFMT)
			{
				case S_IFREG: printf("data file"); break;
				case S_IFDIR: printf("directory"); break;
				case S_IFLNK: printf("symlink -->");
							  if(yaffs_readlink(str,str,100) < 0)
								printf("no alias");
							  else
								printf("\"%s\"",str);
							  break;
				default: printf("unknown"); break;
			}

			printf("\n");

			if((s.st_mode & S_IFMT) == S_IFDIR && recursive)
				dump_directory_tree_worker(str,1);

		}

		yaffs_closedir(d);
	}

}

int dumpDir(const char *dname)
{
	printf("\nDirectory look-up of %s\n\n", dname);

	dump_directory_tree_worker(dname,0);
	printf("\n");
	printf("Free space in %s is %d\n",dname,(int)yaffs_freespace(dname));
	printf("Total space in %s is %d\n\n",dname,(int)yaffs_totalspace(dname));

	return 0;
}


int yaffs_test_format(const char *mountpt)
{
	int ret;

	yaffs_start_up();

	yaffs_unmount(mountpt);

	ret = yaffs_format(mountpt, 0, 0, 0);
	printf("yaffs_format(...,0, 0, 0) of unmounted returned %d\n", ret);

	yaffs_mount(mountpt);

	ret = yaffs_format(mountpt, 0, 0, 0);
	printf("yaffs_format(...,0, 0, 0) of mounted returned %d\n", ret);

	ret = yaffs_format(mountpt, 1, 0, 0);
	printf("yaffs_format(...,1, 0, 0) of mounted returned %d\n", ret);

	ret = yaffs_mount(mountpt);
	printf("mount should return 0 returned %d\n", ret);

	ret = yaffs_format(mountpt, 1, 0, 1);
	printf("yaffs_format(...,1, 0, 1) of mounted returned %d\n", ret);

	ret = yaffs_mount(mountpt);
	printf("mount should return -1 returned %d\n", ret);

	yaffs_unmount(mountpt);

	printf("\nformat test passed.\n");

	return 0;
}

int yaffs_test_mount(const char *mountpt)
{
	int output = 0;
	int output2 = 0;
	char fn[100];

	yaffs_start_up();

	output = yaffs_mount(mountpt);
	if (output >= 0) {  
		printf("yaffs mounted: %s\n", mountpt); 
	} else {
		printf("yaffs failed to mount: %s\n", mountpt);
		return (-1);
	}

	dumpDir(mountpt);

	//now create a file.
	sprintf(fn, "%s/mount_test.txt", mountpt);
	output = yaffs_open(fn, O_CREAT | O_RDWR, S_IREAD | S_IWRITE);
	if (output >= 0) {  
		printf("file created: %s\n", fn); 
	} else {
		printf("yaffs failed to create the file: %s\n", fn);
		return (-1);
	}

	output2 = yaffs_close(output);
	if (output2 >= 0) {  
		printf("file closed: %s\n", fn); 
	} else {
		printf("yaffs failed to close the file: %s\n", fn);
		return (-1);
	}

	dumpDir(mountpt);

	//unmount and remount the mount point.
	output = yaffs_unmount(mountpt);
	if (output >= 0) {  
		printf("yaffs unmounted: %s\n", mountpt); 
	} else {
		printf("yaffs failed to unmount: %s\n", mountpt);
		return (-1);
	}

	output = yaffs_mount(mountpt);
	if (output >= 0) {  
		printf("yaffs mounted: %s\n", mountpt); 
	} else {
		printf("yaffs failed to mount: %s\n", mountpt);
		return (-1);
	}

	dumpDir(mountpt);

	//now open the existing file.
	output = yaffs_open(fn, O_RDWR, S_IREAD | S_IWRITE);
	if (output >= 0) {  
		printf("file created: %s\n", fn); 
	} else {
		printf("yaffs failed to open the file: %s\n", fn);
		return (-1);
	}

	//close the file.
	output2 = yaffs_close(output);
	if (output2 >= 0) {  
		printf("file closed: %s\n", fn); 
	} else {
		printf("yaffs failed to close the file: %s\n", fn);
		return (-1);
	}

	//remove the file
	output = yaffs_unlink(fn);
	if (output >= 0) {
		printf("file removed: %s\n", fn);
	}
	else {
		printf("yaffs failed to remove the file: %s\n", fn);
		return (-1);
	}

	dumpDir(mountpt);

	//unmount the mount point.
	output = yaffs_unmount(mountpt);
	if (output >= 0) {  
		printf("yaffs unmounted: %s\n", mountpt); 
	} else {
		printf("error\n yaffs failed to unmount: %s\nerror\n", mountpt);
		return (-1);
	}

	printf("\nmount test passed.\n");

	return (0);
}

int yaffs_test_io(const char *mountpt)
{
	int output;
	char fn[100];

	yaffs_start_up();

	output = yaffs_mount(mountpt);
	if (output >= 0) {  
		printf("yaffs mounted: %s\n", mountpt); 
	} else {
		printf("yaffs failed to mount: %s\n", mountpt);
		return (-1);
	}
	
	dumpDir(mountpt);

	sprintf(fn, "%s/make_file_test.txt", mountpt);
	make_a_file(fn, 'x', 200);

	sprintf(fn, "%s/make_pattern_file_test.txt", mountpt);
	make_pattern_file(fn, 200);
	check_pattern_file(fn);

	sprintf(fn, "%s/vxworks.elf", mountpt);
	copy_in_a_file(fn, "vxworks.elf");

	dumpDir(mountpt);

	printf("\nio test passed.\n");

	return 0;
}


int yaffs_test_rename(const char *mountpt)
{
	int i;
	char a[100];
	char b[100];
	char c[100];

	yaffs_start_up();

	yaffs_mount(mountpt);

	dumpDir(mountpt);

	sprintf(a,"%s/a",mountpt);
	sprintf(b,"%s/b",mountpt);
	sprintf(c,"%s/c",mountpt);

	i = yaffs_open(a, O_CREAT | O_TRUNC | O_RDWR, S_IREAD | S_IWRITE);
	yaffs_close(i);

	i = yaffs_open(b, O_CREAT | O_TRUNC | O_RDWR,  S_IREAD | S_IWRITE);
	yaffs_close(i);

	dumpDir(mountpt);

	yaffs_rename(a,c); // rename over
	dumpDir(mountpt);

	yaffs_rename(b,a); // rename back again (not renaimng over)
	dumpDir(mountpt);

	yaffs_unmount(mountpt);

	printf("\nrename test passed.\n");

	return 0;
}


int yaffs_test_clear_disk(void)
{
	int ret = 0;

	yaffs_start_up();

	ret = yaffs_unmount(YAFFS_TEST_MOUNT_POINT);

	fsl_nand_chip_erase();

	ret = yaffs_format(YAFFS_TEST_MOUNT_POINT, 0, 0, 0);
	if (ret) {
		printf("%s,%d: yaffs_format %s failed, ret = %d\n",
			__FUNCTION__, __LINE__, YAFFS_TEST_MOUNT_POINT, ret);
		return ret;
	}

	ret = yaffs_mount(YAFFS_TEST_MOUNT_POINT);
	if (ret) {
		printf("%s,%d: yaffs_mount %s failed, ret = %d\n",
			__FUNCTION__, __LINE__, YAFFS_TEST_MOUNT_POINT, ret);
		return ret;
	}

	dumpDir(YAFFS_TEST_MOUNT_POINT);

	return 0;
}


int yaffs_test_fill_disk(void)
{
	int h = 0;
	int ret = 0;
	int free = 0;
	static char xx[600];
	char str[50];

	ret = yaffs_test_clear_disk();
	if (ret) {
		printf("%s,%d: yaffs_test_clear_disk failed, ret = %d\n",
			__FUNCTION__, __LINE__, ret);
		return ret;
	}

	sprintf(str, "%s/fill_disk.txt", YAFFS_TEST_MOUNT_POINT);
	h = yaffs_open(str, O_CREAT | O_RDWR | O_TRUNC, S_IREAD | S_IWRITE);
	printf("%s,%d: writing file %s handle %d\n", __FUNCTION__, __LINE__, str, h);

	while ((ret = yaffs_write(h, xx, 600)) == 600)	
	{
		free = yaffs_freespace(YAFFS_TEST_MOUNT_POINT);
	}

	printf("%s,%d: free space %d\n", __FUNCTION__, __LINE__, free);

	ret = yaffs_close(h);
	printf("%s,%d: close %d\n", __FUNCTION__, __LINE__, ret);

	dumpDir(YAFFS_TEST_MOUNT_POINT);

	ret = yaffs_unlink(str);
	if (ret < 0) {
		printf("%s,%d: delete file %s failed, ret = %d\n",
			__FUNCTION__, __LINE__, str, ret);
	}

	ret = yaffs_test_clear_disk();
	if (ret) {
		printf("%s,%d: yaffs_test_clear_disk failed, ret = %d\n",
			__FUNCTION__, __LINE__, ret);
		return ret;
	}

	return ret;	
}


int yaffs_test_fill_files(void)
{
	int cnt = 0;
	int ret = 0;
	char fn[100];

	ret = yaffs_test_clear_disk();
	if (ret) {
		printf("%s,%d: yaffs_test_clear_disk failed, ret = %d\n",
			__FUNCTION__, __LINE__, ret);
		return ret;
	}

	while (1)
	{
		sprintf(fn, "%s/fill_files_%d.txt", YAFFS_TEST_MOUNT_POINT, cnt);
		ret = make_a_file(fn, cnt, 1*1024*1024);
		if (ret < 0) {
			printf("%s,%d: create file %s failed, ret = %d, cnt = %d\n",
				__FUNCTION__, __LINE__, fn, ret, cnt);
			break;
		}
		cnt++;
	}

	dumpDir(YAFFS_TEST_MOUNT_POINT);
	printf("%s,%d: cnt = %d\n", __FUNCTION__, __LINE__, cnt);

	return 0;
}


int yaffs_test_speed(void)
{
	int ret = 0;
	int f = 0;
	int size = 30*1024*1024;
	char fn[100];
	unsigned char *pBuf = NULL;
	unsigned int startTicks = 0;
	unsigned int endTicks = 0;

	ret = yaffs_test_clear_disk();
	if (ret) {
		printf("%s,%d: yaffs_test_clear_disk failed, ret = %d\n",
			__FUNCTION__, __LINE__, ret);
		return ret;
	}

	sprintf(fn, "%s/speed_test.txt", YAFFS_TEST_MOUNT_POINT);

	pBuf = malloc(size);
	if (!pBuf) {
		printf("%s,%d: malloc %d failed\n", __FUNCTION__, __LINE__, size);
		return -1;
	}

	startTicks = tickGet();

	f = yaffs_open(fn, O_CREAT | O_RDWR | O_TRUNC, S_IREAD | S_IWRITE);
	if (f < 0) {
		printf("%s,%d: open file %s failed\n", __FUNCTION__, __LINE__, fn);
		return -1;
	}

	ret = yaffs_write(f, pBuf, size);
	if (size != ret) {
		printf("%s,%d: problem writing yaffs file, ret = %d\n", __FUNCTION__, __LINE__, ret);
		yaffs_close(f);
		free(pBuf);
		return -1;
	}

	yaffs_close(f);

	endTicks = tickGet();

	printf("%s,%d: writing yaffs file over, size = %d, time = %ds, ret = %d\n",
		__FUNCTION__, __LINE__, size, (endTicks-startTicks)/60, ret);

	dumpDir(YAFFS_TEST_MOUNT_POINT);

	memset(pBuf, 0, size);
	startTicks = tickGet();

	f = yaffs_open(fn, O_RDWR | O_TRUNC, S_IREAD | S_IWRITE);
	if (f < 0) {
		printf("%s,%d: open file %s failed\n", __FUNCTION__, __LINE__, fn);
		return -1;
	}

	while (yaffs_read(f, pBuf, 1) > 0) {
		ret++;
	}

	if (size != ret) {
		printf("%s,%d: problem reading yaffs file, ret = %d\n", __FUNCTION__, __LINE__, ret);
		yaffs_close(f);
		free(pBuf);
		return -1;
	}

	yaffs_close(f);
	free(pBuf);

	endTicks = tickGet();

	printf("%s,%d: reading yaffs file over, size = %d, time = %ds, ret = %d\n",
			__FUNCTION__, __LINE__, size, (endTicks-startTicks)/60, ret);

	return 0;
}


int yaffs_test_add_del(void)
{
	int cnt = 0;
	int ret = 0;
	char fn[100];

	ret = yaffs_test_clear_disk();
	if (ret) {
		printf("%s,%d: yaffs_test_clear_disk failed, ret = %d\n",
			__FUNCTION__, __LINE__, ret);
		return ret;
	}

	/*while (1)*/
	{
		while (cnt < 80)
		{
			sprintf(fn, "%s/test_add_del_4k_%d.txt", YAFFS_TEST_MOUNT_POINT, cnt);
			ret = make_a_file(fn, cnt, 4*1024);
			if (ret < 0) {
				printf("%s,%d: create file %s failed, ret = %d, cnt = %d\n",
					__FUNCTION__, __LINE__, fn, ret, cnt);
			}

			sprintf(fn, "%s/test_add_del_64k_%d.txt", YAFFS_TEST_MOUNT_POINT, cnt);
			ret = make_a_file(fn, cnt, 64*1024);
			if (ret < 0) {
				printf("%s,%d: create file %s failed, ret = %d, cnt = %d\n",
					__FUNCTION__, __LINE__, fn, ret, cnt);
			}

			sprintf(fn, "%s/test_add_del_128k_%d.txt", YAFFS_TEST_MOUNT_POINT, cnt);
			ret = make_a_file(fn, cnt, 128*1024);
			if (ret < 0) {
				printf("%s,%d: create file %s failed, ret = %d, cnt = %d\n",
					__FUNCTION__, __LINE__, fn, ret, cnt);
			}

			sprintf(fn, "%s/test_add_del_1M_%d.txt", YAFFS_TEST_MOUNT_POINT, cnt);
			ret = make_a_file(fn, cnt, 1*1024*1024);
			if (ret < 0) {
				printf("%s,%d: create file %s failed, ret = %d, cnt = %d\n",
					__FUNCTION__, __LINE__, fn, ret, cnt);
			}

			cnt++;
		}

		dumpDir(YAFFS_TEST_MOUNT_POINT);
		cnt = 0;

		while (cnt < 80)
		{
			sprintf(fn, "%s/test_add_del_4k_%d.txt", YAFFS_TEST_MOUNT_POINT, cnt);
			ret = yaffs_unlink(fn);
			if (ret < 0) {
				printf("%s,%d: delete file %s failed, ret = %d\n",
					__FUNCTION__, __LINE__, fn, ret);
			}

			sprintf(fn, "%s/test_add_del_64k_%d.txt", YAFFS_TEST_MOUNT_POINT, cnt);
			ret = yaffs_unlink(fn);
			if (ret < 0) {
				printf("%s,%d: delete file %s failed, ret = %d\n",
					__FUNCTION__, __LINE__, fn, ret);
			}

			sprintf(fn, "%s/test_add_del_128k_%d.txt", YAFFS_TEST_MOUNT_POINT, cnt);
			ret = yaffs_unlink(fn);
			if (ret < 0) {
				printf("%s,%d: delete file %s failed, ret = %d\n",
					__FUNCTION__, __LINE__, fn, ret);
			}

			sprintf(fn, "%s/test_add_del_1M_%d.txt", YAFFS_TEST_MOUNT_POINT, cnt);
			ret = yaffs_unlink(fn);
			if (ret < 0) {
				printf("%s,%d: delete file %s failed, ret = %d\n",
					__FUNCTION__, __LINE__, fn, ret);
			}

			cnt++;
		}

		dumpDir(YAFFS_TEST_MOUNT_POINT);
		cnt = 0;
	}

	return 0;
}


int yaffs_test_main(void)
{
	printf("\nStart test\n");

	printf("\n******************************\n");
	yaffs_test_format(YAFFS_TEST_MOUNT_POINT);
	printf("\n******************************\n");
	yaffs_test_mount(YAFFS_TEST_MOUNT_POINT);
	printf("\n******************************\n");
	yaffs_test_io(YAFFS_TEST_MOUNT_POINT);
	printf("\n******************************\n");
	yaffs_test_rename(YAFFS_TEST_MOUNT_POINT);
	printf("\n******************************\n");
	yaffs_test_fill_disk();
	printf("\n******************************\n");
	yaffs_test_fill_files();
	printf("\n******************************\n");
	yaffs_test_speed();
	printf("\n******************************\n");
	yaffs_test_add_del();
	printf("\n******************************\n");

	printf("\n\nEnd test\n");

	return 0;
}


int yaffs_test_init(void)
{
	return 0;
}

